/**
 * Copyright 2011 Mathieu Magnaudet
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package edu.enac.geoapp.parser.grib;

import java.awt.geom.Rectangle2D;
import java.awt.image.DataBuffer;
import java.awt.image.WritableRaster;
import java.io.IOException;
import java.util.List;

import javax.media.jai.RasterFactory;

import org.apache.log4j.Logger;
import org.geotools.coverage.CoverageFactoryFinder;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridCoverageFactory;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.referencing.CRS;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.FactoryException;

import ucar.ma2.Array;
import ucar.ma2.Index;
import ucar.nc2.Dimension;
import ucar.nc2.NetcdfFile;
import ucar.nc2.constants.FeatureType;
import ucar.nc2.dataset.NetcdfDataset;
import ucar.nc2.dt.GridDatatype;
import ucar.nc2.dt.TypedDatasetFactory;
import ucar.nc2.dt.grid.GeoGrid;
import ucar.nc2.dt.grid.GridDataset;
import ucar.unidata.geoloc.LatLonPoint;
import ucar.unidata.geoloc.LatLonRect;

/**
 * Parser for the file type GRIB
 * @author mathieu
 *
 */
public class GribParser {
	public static int T = 0;
	public static int UV = 1;
	private int type;
	private int nbOfDate;
	private int nbOfIsobaric;
	private GridCoverage2D[][] rasterizedData;
	private GridCoverageFactory gcFactory = CoverageFactoryFinder
			.getGridCoverageFactory(null);
	private List<GridDatatype> gridList;
	
	private Logger logger = Logger.getLogger(GribParser.class);
	static {
		System.setProperty("com.sun.media.jai.disableMediaLib", "true");
	}

	/**
	 * Parse the given file
	 * @param path path to the file
	 * @param type type of the grib file {@link GribParser.T} or {@link GribParser.UV}
	 * @throws IOException if the file cannot be read
	 */
	public void parse(String path, int type) throws IOException {
		this.type = type;
		NetcdfFile ncfile = NetcdfFile.open(path);
		NetcdfDataset data = new NetcdfDataset(ncfile);
		StringBuilder errlog = new StringBuilder();

		for (Dimension dim : ncfile.getDimensions()) {
			if (dim.getName().equals("time"))
				nbOfDate = dim.getLength();
			if (dim.getName().equals("isobaric"))
				nbOfIsobaric = dim.getLength();
		}

		GridDataset gridDataset = (GridDataset) TypedDatasetFactory.open(
				FeatureType.GRID, data, null, errlog);

		gridList = gridDataset.getGrids();
		ncfile.close();
	}

	/**
	 * Return the data in an array of {@link GridCoverage2D}
	 * @return the data in an array of {@link GridCoverage2D}
	 * @throws IOException
	 * @throws MismatchedDimensionException
	 * @throws FactoryException
	 */
	public GridCoverage2D[][] getData() throws IOException,
			MismatchedDimensionException, FactoryException {
		rasterizedData = new GridCoverage2D[nbOfDate][nbOfIsobaric];
		logger.info("Number of geogrid:" + gridList.size());
		GeoGrid grid = (GeoGrid) gridList.get(0);
		int min = 0;
		int max = 0;
		for (int date = 0; date < nbOfDate; date++) {
			for (int isobaric = 0; isobaric < nbOfIsobaric; isobaric++) {
				int height = grid.getYDimension().getLength();
				int width = grid.getXDimension().getLength();
				int heightIndex = grid.getYDimensionIndex() - 1;

				WritableRaster raster = RasterFactory.createBandedRaster(
						DataBuffer.TYPE_FLOAT, width, height, 1, null);
				Array dataArray = grid.readYXData(date, isobaric);
				Index dataIndex = dataArray.getIndex();

				int x = 0;
				int y = 0;
				
				try {

					for (y = 0; y < width; y++) {
						for (x = 0; x < height; x++) {
							int val;
							Index i;
							if (heightIndex == 0) {
								i = dataIndex.set(y, x);
							} else {
								i = dataIndex.set(x, y);
							}
							val = dataArray.getInt(i);
							if(date==0 && isobaric ==0)
								min = max = val;
							else if(val > -9999){
								min = Math.min(min, val);
								max = Math.max(max, val);
							}
							raster.setSample(y, x, 0, val);
						}
					}

				} catch (Exception e) {
					System.out.println(e);
					e.printStackTrace();
					System.out.println(x + " , " + y);
					return null;
				}
				
				LatLonRect rect = grid.getCoordinateSystem()
						.getLatLonBoundingBox();

				LatLonPoint llCorner = rect.getLowerLeftPoint();
				LatLonPoint urCorner = rect.getUpperRightPoint();

				GeneralEnvelope env = new GeneralEnvelope(
						new Rectangle2D.Double(llCorner.getLongitude(),
								llCorner.getLatitude(), Math.abs(llCorner
										.getLongitude()
										- urCorner.getLongitude()),
								Math.abs(llCorner.getLatitude()
										- urCorner.getLatitude())));
				String HRAPSTEREO_WKT = "GEOGCS[\"Coordinate System imported from GRIB file\","
						+ "DATUM[\"unknown\",SPHEROID[\"Sphere\",6367470,0]],PRIMEM[\"Greenwich\",0],"
						+ "UNIT[\"degree\",0.0174532925199433]]";

				env.setCoordinateReferenceSystem(CRS.parseWKT(HRAPSTEREO_WKT));
				GridCoverage2D gc = gcFactory.create("Original", raster, env);
				rasterizedData[date][isobaric] = gc;

			}
		
		}
		logger.info("min = " + min + " max = "+ max);
		return rasterizedData;
	}

	/**
	 * @return the type
	 */
	public int getType() {
		return type;
	}

	/**
	 * @param type
	 *            the type to set
	 */
	public void setType(int type) {
		this.type = type;
	}

	/**
	 * @return the nbOfDate
	 */
	public int getNbOfDate() {
		return nbOfDate;
	}

	/**
	 * @param nbOfDate
	 *            the nbOfDate to set
	 */
	public void setNbOfDate(int nbOfDate) {
		this.nbOfDate = nbOfDate;
	}

	/**
	 * @return the nbOfIsobaric
	 */
	public int getNbOfIsobaric() {
		return nbOfIsobaric;
	}

	/**
	 * @param nbOfIsobaric
	 *            the nbOfIsobaric to set
	 */
	public void setNbOfIsobaric(int nbOfIsobaric) {
		this.nbOfIsobaric = nbOfIsobaric;
	}

}
